import { copyNewRes, MyRequest, MyResponse, sendMyHttpRequest } from "./config";
import qs from "qs";

type RequestStatus = "none" | "loading" | "success";

type TypeWaitCallback = (res: MyResponse<any>) => void;

export class CachePool {
  private static instance: CachePool;

  private cache: Map<string, MyResponse<any>> | undefined;

  private statusMap: Map<string, RequestStatus> | undefined;

  private callbackMap: Map<string, TypeWaitCallback[]> | undefined;

  private constructor() {
    this.cache = new Map();
    this.statusMap = new Map();
    this.callbackMap = new Map();
  }

  public static getInstance(): CachePool {
    if (!CachePool.instance) {
      CachePool.instance = new CachePool();
    }
    return CachePool.instance;
  }

  public generateKey(request: MyRequest): string {
    const { method = "get" } = request;
    return `${method}::${request.url}?${qs.stringify(request.params)}`;
  }

  public getCache(requestOption: MyRequest): Promise<MyResponse<any>> {
    const key = this.generateKey(requestOption);

    const status = this.statusMap!.get(key);

    console.log("--getCache--", status);

    if (status === "success") {
      if (this.cache?.has(key)) {
        console.log("success", this.cache!.get(key));
        return Promise.resolve(copyNewRes(this.cache!.get(key)!));
      }
      return Promise.reject(new Error(`cache::${key} is undefined`));
    }

    if (status === "loading") {
      return new Promise((resolve) => {
        if (this.callbackMap?.has(key)) {
          this.callbackMap!.get(key)!.push((res) => {
            resolve(res);
          });
        } else {
          this.callbackMap!.set(key, [
            (res) => {
              resolve(res);
            },
          ]);
        }

        console.log("callbackMap", this.callbackMap);
      });
    }

    this.statusMap!.set(key, "loading");
    return sendMyHttpRequest({
      ...requestOption,
      needCache: false,
    }).then((res) => {
      if (res.success) {
        this.statusMap!.set(key, "success");
        this.cache!.set(key, res);
      } else {
        this.statusMap!.set(key, "none");
      }
      if (this.callbackMap!.has(key)) {
        const arr = this.callbackMap!.get(key)!;
        arr.forEach((cb) => {
          cb(copyNewRes(res));
        });
      }

      return copyNewRes(res);
    });
  }
}
